package com.ihy.filter;

import com.alibaba.fastjson.JSON;
import com.ihy.domain.User;
import com.ihy.utils.AgentUserKit;
import com.ihy.utils.JwtTokenUtils;
import com.ihy.utils.RedisTemplateUtil;
import com.ihy.vo.ResultResponse;
import com.sun.istack.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * token过滤器
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Qualifier("userDetailsServiceImpl")
    @Autowired
    private UserDetailsService userDetailsService;

    RequestHeaderRequestMatcher requiresAuthenticationRequestMatcher;

    @Autowired
    RedisTemplateUtil redisTemplateUtil;
    private ResultResponse resultResponse;

    @Autowired
    public void setResultResponse(ResultResponse resultResponse) {
        this.resultResponse = resultResponse;
    }

    @Autowired
    public JwtAuthenticationTokenFilter() {
        //拦截header中带Authorization的请求
        this.requiresAuthenticationRequestMatcher = new RequestHeaderRequestMatcher("Authorization");
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, @NotNull FilterChain chain) throws ServletException, IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf8");
        String token = request.getHeader("Authorization");
        log.info("token内容: [{}]", token);
        //header中没有信息直接放行
        if (!requiresAuthenticationRequestMatcher.matches(request)) {
            log.info("访问 {} 接口的请求略过token过滤器", request.getRequestURI());
            chain.doFilter(request, response);
            return;
        }
        Map<String, String> map = AgentUserKit.getOsAndBrowserInfo(request);
        String os = map.get("os");
        //判断token信息不为空
        if (!ObjectUtils.isEmpty(token)) {
            User user;
            try {
                user = JwtTokenUtils.checkToken(token);
            } catch (NullPointerException e) {
                resultResponse.setCode(407);
                resultResponse.setData(null);
                resultResponse.setMessage("token错误");
                log.info("token错误  token内容：[{}]", token);
                response.getWriter().write(JSON.toJSONString(resultResponse));
                return;
            }
            if (user.getUsername() != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                //根据用户名获取用户主体
                UserDetails userDetails = userDetailsService.loadUserByUsername(user.getUsername());
                //获取redis中的用户token
                String userToken = (String) redisTemplateUtil.get(user.getUsername() + os + "Token");
                //如果当前请求到注销接口跳过验证token (解决token超时后点击注销后会弹出超时提醒的问题)
                if (!request.getRequestURI().equals("/IHY/service") && !request.getRequestURI().equals("/IHY/logout") && !request.getRequestURI().equals("/IHY/Login/signIn")) {
                    if (userToken == null) {
                        resultResponse.setCode(407);
                        resultResponse.setData("token过期或不存在");
                        resultResponse.setMessage("会话超时请重新登录");
                        log.info("token过期或不存在");
                        response.getWriter().write(JSON.toJSONString(resultResponse));
                    }
                }
                if (userToken != null && !userToken.equals("")) {
                    //校验
                    if (user.getUsername().equals(userDetails.getUsername()) && token.equals(userToken)) {
                        // 将用户信息存入 authentication，方便后续校验
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        // 将 authentication 存入 ThreadLocal，方便后续获取用户信息
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                        //将token信息存入redis (用户每一次的操作刷新token 解决用户操作时限问题)
                        redisTemplateUtil.set(userDetails.getUsername() + os + "Token", token, 3600);
                        chain.doFilter(request, response);
                    } else {
                        resultResponse.setCode(407);
                        resultResponse.setData("token被弃用");
                        resultResponse.setMessage("已被重新登录");
                        log.info("token被弃用: 已有新用户登录");
                        response.getWriter().write(JSON.toJSONString(resultResponse));
                    }
                }
            }
        } else {
            resultResponse.setCode(407);
            resultResponse.setData("阻止匿名访问");
            resultResponse.setMessage("请求需要token");
            log.info("请求需要携带token");
            response.getWriter().write(JSON.toJSONString(resultResponse));
        }
    }
}